﻿using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Text;

namespace TcpJsonService.Core
{
    /// <summary>
    /// 文件日志提供者
    /// </summary>
    public class FileSizeLoggerProvider : ILoggerProvider
    {
        /// <summary>
        /// 文件日志
        /// </summary>
        private static ILogger _logger;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="configuration">配置</param>
        public FileSizeLoggerProvider(IConfiguration configuration)
        {
            if (_logger == null)
            {
                string level = configuration.GetValue("Logging:Level", "Information");
                _ = Enum.TryParse(level, out LogLevel defaultLevel);
                string directory = configuration.GetValue("Logging:Directory", "logs");
                long size = configuration.GetValue("Logging:Size", 100);
                _logger = new FileSizeLogger(defaultLevel, directory, size);
            }
        }

        public ILogger CreateLogger(string categoryName)
        {
            return _logger;
        }

        public void Dispose()
        {
            GC.SuppressFinalize(this);
        }
    }

    /// <summary>
    /// 按大小存放的文件日志
    /// </summary>
    public class FileSizeLogger : ILogger
    {
        /// <summary>
        /// 默认级别
        /// </summary>
        private readonly LogLevel _defaultLevel;

        /// <summary>
        /// 文件名
        /// </summary>
        private readonly string _fileName;

        /// <summary>
        /// 备份文件名
        /// </summary>
        private readonly string _bakFileName;

        /// <summary>
        /// 保存大小
        /// </summary>
        private readonly long _size;

        /// <summary>
        /// 同步锁
        /// </summary>
        private readonly object _lockObj = new();

        /// <summary>
        /// 文件流 
        /// </summary>
        private FileStream _fs;

        /// <summary>
        /// 文件流写入
        /// </summary>
        private StreamWriter _sw;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="defaultLevel">默认级别</param>
        /// <param name="directory">存放目录</param>
        /// <param name="size">日志大小(mb)</param>
        public FileSizeLogger(LogLevel defaultLevel, string directory, long size)
        {
            _defaultLevel = defaultLevel;
            _fileName = Path.Combine(directory, $"service.log");
            _bakFileName = Path.Combine(directory, $"service_bak.log");
            _size = size * 1024 * 1024;

            //创建文件目录
            if (!Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
            }

            Open();
        }

        /// <summary>
        /// 打开文件
        /// </summary>
        private void Open()
        {
            _fs = new FileStream(_fileName, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
            _sw = new StreamWriter(_fs, Encoding.UTF8) { AutoFlush = true };
        }

        /// <summary>
        /// 关闭文件
        /// </summary>
        private void Close()
        {
            if (_sw != null && _fs != null)
            {
                _sw.Close();
                _fs.Close();
            }
        }

        #region 实现ILogger
        public virtual void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            if (IsEnabled(logLevel))
            {
                lock (_lockObj)
                {
                    try
                    {
                        if (_fs.Length > _size)
                        {
                            Close();
                            File.Move(_fileName, _bakFileName, true);
                            Open();
                        }
                        _sw.WriteLine(exception == null
                           ? $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}][{(int)logLevel}] {state}"
                           : $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}][{(int)logLevel}] {state}\n{exception}");
                    }
                    catch
                    {
                    }
                }
            }
        }

        public bool IsEnabled(LogLevel logLevel)
        {
            return logLevel >= _defaultLevel;
        }

        public IDisposable BeginScope<TState>(TState state)
        {
            return null;
        }
        #endregion

    }
}
